﻿#region Copyright (c) 2000-2008 Developer Express Inc.
/*
{*******************************************************************}
{                                                                   }
{       Developer Express .NET Component Library                    }
{       AgDataGrid                                 }
{                                                                   }
{       Copyright (c) 2000-2008 Developer Express Inc.              }
{       ALL RIGHTS RESERVED                                         }
{                                                                   }
{   The entire contents of this file is protected by U.S. and       }
{   International Copyright Laws. Unauthorized reproduction,        }
{   reverse-engineering, and distribution of all or any portion of  }
{   the code contained in this file is strictly prohibited and may  }
{   result in severe civil and criminal penalties and will be       }
{   prosecuted to the maximum extent possible under the law.        }
{                                                                   }
{   RESTRICTIONS                                                    }
{                                                                   }
{   THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES           }
{   ARE CONFIDENTIAL AND PROPRIETARY TRADE                          }
{   SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS   }
{   LICENSED TO DISTRIBUTE THE PRODUCT AND ALL ACCOMPANYING .NET    }
{   CONTROLS AS PART OF AN EXECUTABLE PROGRAM ONLY.                 }
{                                                                   }
{   THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED      }
{   FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE        }
{   COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE       }
{   AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT  }
{   AND PERMISSION FROM DEVELOPER EXPRESS INC.                      }
{                                                                   }
{   CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON       }
{   ADDITIONAL RESTRICTIONS.                                        }
{                                                                   }
{*******************************************************************}
*/
#endregion Copyright (c) 2000-2008 Developer Express Inc.

using System.Collections;
using System.Collections.Generic;
using System;
using System.Reflection;
using DevExpress.AgDataGrid.Data.Helpers;
using System.Collections.Specialized;
using System.Globalization;
namespace DevExpress.AgDataGrid.Data {
	public class DataController {
		public const int InvalidRow = Int32.MinValue + 1;
		DataHelperBase helper;
		object dataSource;
		DataColumnSortInfoCollection sortInfo;
		ListSourceRowIndexCollection controllerRowIndexes;
		VisibleIndexCollection visibleRowIndexes;
		ValueComparer comparer;
		GroupRowInfoCollection groupInfo;
		IDataControllerVisualClient visualClient;
		int focusedControllerRowIndex;
		EditingRowInfo editingInfo;
		SummaryItemCollection totalSummary, groupSummary;
		CustomSummaryEventArgs summaryCalculateArgs;
		SelectionController selectionController;
		GroupRowsKeeper groupRowsKeeper;		
		public DataController() {
			this.visualClient = new NullVisualClient();
			this.sortInfo = new DataColumnSortInfoCollection();
			SortInfo.CollectionChanged += OnSortInfoCollectionChanged;
			this.selectionController = new SelectionController(this);
			this.controllerRowIndexes = new ListSourceRowIndexCollection();
			this.groupInfo = CreateGroupInfo();
			this.visibleRowIndexes = new VisibleIndexCollection(GroupInfo);
			this.comparer = CreateComparer();
			this.totalSummary = new SummaryItemCollection();
			TotalSummary.CollectionChanged += OnTotalSummaryCollectionChanged;
			this.groupSummary = new SummaryItemCollection();
			this.groupRowsKeeper = new GroupRowsKeeper(this);
			GroupSummary.CollectionChanged += OnGroupSummaryCollectionChanged;
		}
		protected internal EditingRowInfo EditingInfo { get { return editingInfo; } }
		public SelectionController SelectionController { get { return selectionController; } }
		public int FocusedRowVisibleIndex {
			get { return GetVisibleIndex(FocusedControllerRowIndex); }
			set {
				if(value >= VisibleRowCount) value = VisibleRowCount - 1;
				if(value < 0) value = 0;
				FocusedControllerRowIndex = GetControllerRowHandle(value);
			}
		}
		public int FocusedControllerRowIndex {
			get {
				if(VisibleListSourceRowCount == 0) return InvalidRow;
				return focusedControllerRowIndex;
			}
			set {
				if(!IsValidControllerRowHandle(value)) {
					if(IsValidControllerRowHandle(focusedControllerRowIndex)) return;
					value = GetControllerRowHandle(0);
				}
				if(FocusedControllerRowIndex == value) return;
				if(!OnFocusedControllerRowIndexChanging(FocusedControllerRowIndex, value)) return;
				int oldControllerRowIndex = focusedControllerRowIndex;
				focusedControllerRowIndex = value;
				OnFocusedControllerRowIndexChanged(oldControllerRowIndex, value);
			}
		}
		protected virtual void OnFocusedControllerRowIndexChanged(int oldControllerRowIndex, int newControllerRowIndex) {
			VisualClient.FocusedRowChanged(oldControllerRowIndex, newControllerRowIndex);
		}
		protected virtual bool OnFocusedControllerRowIndexChanging(int oldControllerRowIndex, int newControllerRowIndex) {
			bool res = VisualClient.FocusedRowChanging(oldControllerRowIndex, newControllerRowIndex);
			if(res) {
				CancelEdit();
			}
			return res;
		}
		protected GroupRowsKeeper GroupRowsKeeper { get { return groupRowsKeeper; } }
		protected internal CustomSummaryEventArgs SummaryCalculateArgs {
			get {
				if(summaryCalculateArgs == null)
					summaryCalculateArgs = new CustomSummaryEventArgs(this);
				return summaryCalculateArgs;
			}
		}
		protected internal DataHelperBase Helper {
			get {
				if(helper == null)
					helper = CreateDataHelper();
				return helper;
			}
		}
		public virtual object DataSource {
			get { return dataSource; }
			set {
				if(dataSource == value) return;
				dataSource = value;
				OnDataSourceChanged();
			}
		}
		public IDataControllerVisualClient VisualClient {
			get { return visualClient; }
			set {
				if(value == visualClient) return;
				if(value == null) value = new NullVisualClient();
				visualClient = value;
			}
		}
		public virtual DataColumnCollection Columns { get { return Helper.Columns; } }
		public DataColumnSortInfoCollection SortInfo { get { return sortInfo; } }
		public SummaryItemCollection TotalSummary { get { return totalSummary; } }
		public SummaryItemCollection GroupSummary { get { return groupSummary; } }
		public bool IsSorted { get { return SortInfo.Count > 0; } }
		public bool IsGrouped { get { return SortInfo.GroupCount > 0; } }
		protected ValueComparer Comparer { get { return comparer; } }
		public GroupRowInfoCollection GroupInfo { get { return groupInfo; } }
		protected ListSourceRowIndexCollection ControllerRowIndexes { get { return controllerRowIndexes; } }
		protected VisibleIndexCollection VisibleRowIndexes { get { return visibleRowIndexes; } }
		public int VisibleRowCount { get { return !IsGrouped ? VisibleListSourceRowCount : VisibleRowIndexes.Count; } }
		public virtual int ListSourceRowCount { get { return Helper.RowCount; } }
		public virtual int VisibleListSourceRowCount { get { return ControllerRowIndexes.IsEmpty ? ListSourceRowCount : ControllerRowIndexes.Count; } }
		public virtual object GetListSourceRow(int listIndex) { return Helper.GetRow(listIndex); }
		public virtual object GetListSourceRowValue(int listIndex, int columnIndex) {
			if(columnIndex < 0 || columnIndex >= Columns.Count) return null;
			return GetListSourceRowValue(listIndex, Columns[columnIndex]);
		}
		public virtual bool IsAllowEditing { get { return IsAllowRowEditing(FocusedControllerRowIndex); } }
		public virtual bool IsAllowSorting(string columnName) { return IsAllowSorting(Columns[columnName]); }
		public virtual bool IsAllowSorting(DataColumn column) {
			if(column == null) return false;
			return !column.Type.IsArray;
		}
		public virtual bool IsAllowRowEditing(int controllerRow) {
			return IsValidControllerRowHandle(controllerRow) && !IsGroupRowHandle(controllerRow) && VisualClient.RowEditing(controllerRow);
		}
		public virtual bool BeginEdit() {
			if(!IsAllowRowEditing(FocusedControllerRowIndex)) return false;
			if(IsEditing) return true;
			this.editingInfo = SaveRowValues(FocusedControllerRowIndex);
			return EditingInfo != null;
		}
		public virtual bool IsEditing { get { return EditingInfo != null; } }
		public virtual void CancelEdit() {
			if(!IsEditing) return;
			VisualClient.RowEdited(FocusedControllerRowIndex, EditingInfo.SavedValues, true);
			RestoreValues(EditingInfo);
			this.editingInfo = null;
		}
		protected virtual EditingRowInfo SaveRowValues(int controllerRow) {
			int index = GetListSourceRowIndex(controllerRow);
			if(!Helper.IsValidListIndex(index)) return null;
			EditingRowInfo res = new EditingRowInfo(controllerRow, index, GetListSourceRow(index));
			foreach(DataColumn column in Columns) {
				if(column.ReadOnly) continue;
				res.SavedValues.Add(column, GetListSourceRowValue(index, column));
			}
			return res;
		}
		protected virtual void RestoreValues(EditingRowInfo editingInfo) {
			if(editingInfo == null) return;
			foreach(KeyValuePair<DataColumn, object> pair in editingInfo.SavedValues) {
				try {
					pair.Key.SetValue(editingInfo.ListIndex, editingInfo.EditingRow, pair.Value);
				} catch { }
			}
		}
		public virtual void EndEdit() {
			if(!IsEditing) return;
			VisualClient.RowEdited(FocusedControllerRowIndex, EditingInfo.SavedValues, false);
			this.editingInfo = null;
		}
		public virtual void DeleteRow(int controllerRow) {
			if(!IsDataRowHandle(controllerRow)) return;
			Helper.DeleteRow(GetListSourceRowIndex(controllerRow));
		}
		public virtual void SetRowValue(int controllerRow, DataColumn column, object value) {
			SetListSourceRowValue(GetListSourceRowIndex(controllerRow), column, value);
		}
		public virtual void SetRowValueWithConversion(DataColumn column, object value) {
			SetListSourceRowValue(GetListSourceRowIndex(FocusedControllerRowIndex), column, column.ConvertValue(value));
		}
		public virtual void SetListSourceRowValue(int listIndex, DataColumn column, object value) {
			if(column == null) return;
			Helper.SetRowValue(listIndex, column, value);
		}
		public virtual object GetListSourceRowValue(int listIndex, string columnName) {
			return Helper.GetRowValue(listIndex, Columns[columnName]);
		}
		public virtual object GetListSourceRowValue(int listIndex, DataColumn column) {
			if(column == null) return null;
			return Helper.GetRowValue(listIndex, column);
		}
		public virtual object GetRowValue(int controllerRow) {
			return GetListSourceRow(GetListSourceRowIndex(controllerRow));
		}
		public virtual object GetRowValue(int controllerRow, DataColumn column) {
			return GetListSourceRowValue(GetListSourceRowIndex(controllerRow), column);
		}
		public virtual object GetRowValue(int controllerRow, string columnName) {
			return GetListSourceRowValue(GetListSourceRowIndex(controllerRow), columnName);
		}
		public object GetVisibleRowValue(int visibleIndex, string columnName) {
			return GetRowValue(GetControllerRowHandle(visibleIndex), columnName);
		}
		public object GetGroupRowValue(int controllerRow) {
			GroupRowInfo group = GroupInfo.GetGroupRowInfoByControllerRowHandle(controllerRow);
			if(group == null) return null;
			return GetGroupRowValue(group);
		}
		public virtual object GetGroupRowValue(GroupRowInfo group) {
			if(sortInfo.GroupCount < group.Level + 1) return null;
			return GetRowValue(group.ChildControllerRow, SortInfo[group.Level].Column);
		}
		public int MoveNextRow(int controllerRow, int diff) {
			if(!IsValidControllerRowHandle(controllerRow)) return InvalidRow;
			int visibleIndex = GetVisibleIndex(controllerRow);
			visibleIndex += diff;
			if(visibleIndex < 0) visibleIndex = 0;
			if(visibleIndex >= VisibleRowCount) visibleIndex = VisibleRowCount - 1;
			return GetControllerRowHandle(visibleIndex);
		}
		public int GetControllerRowLevel(int controllerRow) {
			if(!IsValidControllerRowHandle(controllerRow)) return -1;
			if(!IsGrouped) return 0;
			return GroupInfo.GetControllerRowLevel(controllerRow);
		}
		public virtual int GetControllerRow(int listSourceRow) {
			int res = ControllerRowIndexes.GetControllerIndex(listSourceRow);
			return res < 0 ? InvalidRow : res;
		}
		public int GetControllerRowHandle(int visibleIndex) {
			if(visibleIndex < 0 || visibleIndex >= VisibleRowCount) return InvalidRow;
			if(!IsGrouped) return visibleIndex;
			return VisibleRowIndexes[visibleIndex];
		}
		public int GetVisibleIndex(int controllerRow) {
			if(!IsValidControllerRowHandle(controllerRow)) return InvalidRow;
			if(!IsGrouped) return controllerRow;
			int res = VisibleRowIndexes.IndexOf(controllerRow);
			return res < 0 ? InvalidRow : res;
		}
		public virtual bool IsValidControllerRowHandle(int controllerRow) {
			if(controllerRow == InvalidRow) return false;
			if(IsGroupRowHandle(controllerRow)) {
				return GroupInfo.GetGroupRowInfoByHandle(controllerRow) != null;
			}
			return controllerRow < VisibleListSourceRowCount;
		}
		public virtual bool IsGroupRowHandle(int controllerRow) {
			return GroupRowInfo.IsGroupRowHandle(controllerRow);
		}
		public virtual bool IsDataRowHandle(int controllerRow) {
			return IsValidControllerRowHandle(controllerRow) && !IsGroupRowHandle(controllerRow);
		}
		protected void CheckFocusedControllerRow() {
			FocusedControllerRowIndex = FocusedControllerRowIndex;
		}
		protected virtual void OnSortInfoCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
			DoRefresh();
		}
		protected virtual void OnTotalSummaryCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
			if(e.Action != NotifyCollectionChangedAction.Remove)
				CalculateTotalSummary();
			VisualClientUpdateLayout();
		}
		protected virtual void OnGroupSummaryCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
			if(e.Action != NotifyCollectionChangedAction.Remove)
				CalculateGroupSummary();
			VisualClientUpdateLayout();
		}
		protected virtual void VisualClientUpdateLayout() {
			VisualClient.UpdateLayout();
		}
		int updateCount = 0;
		public void BeginUpdate() {
			this.updateCount++;
		}
		public void EndUpdate() {
			CancelUpdate();
			if(this.updateCount == 0) {
				DoRefresh();
			}
		}
		public void CancelUpdate() {
			if(updateCount <= 0) return;
			this.updateCount--;
		}
		protected bool IsUpdating { get { return this.updateCount > 0; } }
		public virtual void DoRefresh() {
			if(IsUpdating) return;
			GroupRowsKeeper.SaveGroupsState();
			CancelEdit();
			if(DataSource != null)
				OnBeforeRefresh();
			ControllerRowIndexes.IsEmpty = true;
			GroupInfo.Clear();
			VisibleRowIndexes.Clear();
			DoFilterRows();
			DoSortRows();
			DoGroupRows();
			GroupRowsKeeper.RestoreGroupsState();
			BuildVisibleIndexes();
			CalculateSummary();
			VisualClientUpdateLayout();
		}
		protected virtual void OnBeforeRefresh() {
			int columnCount = Columns.Count;
			VisualClient.OnBeforeRefresh();
		}
		protected internal virtual bool OnRowDeleting(int listSourceRow) {
			if(!VisualClient.RowDeleting(listSourceRow)) return false;
			if(GetListSourceRowIndex(FocusedControllerRowIndex) == listSourceRow) {
				int visibleIndex = GetVisibleIndex(FocusedControllerRowIndex);
				if(visibleIndex >= VisibleRowCount - 1) {
					visibleIndex = visibleIndex - 1;
					visibleIndex = Math.Max(0, visibleIndex);
					FocusedControllerRowIndex = GetControllerRowHandle(visibleIndex);
				}
			}
			return true;
		}
		protected internal virtual void OnRowDeleted(int listSourceRow) {			
			DoRefresh();
			VisualClient.RowDeleted(listSourceRow);
		}
		protected internal virtual void OnSelectionChanged(SelectionChangedEventArgs e) {
			VisualClient.SelectionChanged(e);
		}
		protected virtual void DoFilterRows() {
		}
		protected virtual void DoSortRows() {
			if(!IsSorted) return;
			if(ControllerRowIndexes.IsEmpty)
				ControllerRowIndexes.CreateList(ListSourceRowCount);
			ControllerRowIndexes.Sort(CompareRows);
		}
		protected virtual void DoGroupRows() {
			if(!IsGrouped) return;
			DoGroupColumn(SortInfo, GroupInfo, 0, VisibleListSourceRowCount, null);
			GroupInfo.UpdateIndexes();
		}
		protected internal virtual void DoGroupColumn(DataColumnSortInfoCollection sortInfo, GroupRowInfoCollection groupInfo, int controllerRow, int rowCount, GroupRowInfo parentGroup) {
			int level = (parentGroup == null ? 0 : parentGroup.Level + 1);
			DataColumn column = sortInfo[level].Column;
			int prevStart = controllerRow;
			object prevVal = null, val;
			GroupRowInfo groupRow = null;
			for(int row = 0; row < rowCount + 1; row++) {
				if(row != rowCount) {
					int listSourceRow = GetListSourceRowIndex(controllerRow + row);
					val = Helper.GetRowValue(listSourceRow, column);
					if(row > 0) {
						if(IsEqualGroupValues(prevVal, val)) {
							groupRow.ChildControllerRowCount++;
							continue;
						}
					}
					prevVal = val;
				}
				if(row > 0 && !groupInfo.IsLastLevel(groupRow)) { 
					DoGroupColumn(sortInfo, groupInfo, prevStart, groupRow.ChildControllerRowCount, groupRow);
					prevStart = controllerRow + row;
				}
				if(row == rowCount) break;
				groupRow = groupInfo.Add(level, row + controllerRow, parentGroup);
			}
		}
		protected virtual void CalculateSummary() {
			CalculateGroupSummary();
			CalculateTotalSummary();
		}
		protected virtual void CalculateGroupSummary() {
			foreach(SummaryItem item in GroupSummary) {
				foreach(GroupRowInfo groupInfo in GroupInfo) {
					object value = CalcSummaryValue(groupInfo, item, groupInfo.ChildControllerRow, groupInfo.ChildControllerRowCount);
					groupInfo.SetSummaryValue(item, value);
				}
			}
		}
		protected virtual void CalculateTotalSummary() {
			foreach(SummaryItem item in TotalSummary)
				item.SummaryValue = CalcSummaryValue(null, item, 0, VisibleListSourceRowCount);
		}
		protected virtual object CalcSummaryValue(GroupRowInfo groupRow, SummaryItem summaryItem, int controllerRow, int rowCount) {
			if(summaryItem.IsNoneSummary) return 0;
			if(!VisualClient.IsCustomSummaryExists(groupRow, summaryItem)) return null;
			if(summaryItem.IsCustomSummary)
				return CalcCustomSummaryValue(groupRow, summaryItem, controllerRow, rowCount);
			else
				return CalcSummaryValueCore(summaryItem, controllerRow, rowCount);
		}
		protected virtual object CalcCustomSummaryValue(GroupRowInfo groupRow, SummaryItem summaryItem, int controllerRow, int rowCount) {
			SummaryCalculateArgs.Setup(0, null, null, groupRow, CustomSummaryProcess.Start, summaryItem.Key);
			object result = VisualClient.CalcCustomSummary(null, SummaryCalculateArgs);
			if(!SummaryCalculateArgs.TotalValueReady && summaryItem.Column != null) {
				SummaryCalculateArgs.SetupSummaryProcess(CustomSummaryProcess.Calculate);
				result = CalcSummaryValueCore(summaryItem, controllerRow, rowCount);
			}
			SummaryCalculateArgs.SetupSummaryProcess(CustomSummaryProcess.Finalize);
			return VisualClient.CalcCustomSummary(null, SummaryCalculateArgs);
		}
		protected virtual object CalcSummaryValueCore(SummaryItem summaryItem, int controllerRow, int rowCount) {
			if(!summaryItem.AllowCalculate) return null;
			if(summaryItem.SummaryType == SummaryItemType.Count) return rowCount;
			decimal resultNumber = 0, numericVal;
			object result = null, val;
			DataColumn column = summaryItem.Column;
			for(int n = controllerRow; n < controllerRow + rowCount; n++) {
				val = summaryItem.ConvertValue(GetRowValue(n, column), out numericVal);
				switch(summaryItem.SummaryType) {
					case SummaryItemType.Min:
					case SummaryItemType.Max:
						if(n == controllerRow) {
							result = val;
							break;
						}
						if(!(val is IComparable)) val = null;
						if((Comparer.Compare(result, val) * (summaryItem.SummaryType == SummaryItemType.Max ? -1 : 1)) > 0)
							result = val;
						break;
					case SummaryItemType.Custom:
						SummaryCalculateArgs.SetupCell(n, val);
						result = VisualClient.CalcCustomSummary(summaryItem, SummaryCalculateArgs);
						break;
					default:
						resultNumber += numericVal;
						break;
				}
			}
			if(summaryItem.RequireValueConvert)
				result = resultNumber;
			if(summaryItem.SummaryType == SummaryItemType.Average && rowCount > 0)
				result = resultNumber / rowCount;
			return result;
		}
		public virtual void RefreshSummaryValues() {
			CalculateSummary();
		}
		protected virtual int CompareRows(int listSourceRow1, int listSourceRow2) {
			if(listSourceRow1 == listSourceRow2) return 0;
			if(SortInfo.Count == 0) listSourceRow1.CompareTo(listSourceRow2);
			ColumnSortOrder sortOrder = ColumnSortOrder.Ascending;
			int res = 0;
			for(int i = 0; i < SortInfo.Count; i++) {
				sortOrder = SortInfo[i].SortOrder;
				object v1 = GetListSourceRowValue(listSourceRow1, SortInfo[i].Column),
					v2 = GetListSourceRowValue(listSourceRow2, SortInfo[i].Column);
				res = Comparer.Compare(v1, v2);
				if(res != 0) break;
			}
			if(res == 0) res = listSourceRow1.CompareTo(listSourceRow2);
			if(sortOrder == ColumnSortOrder.Descending) res = -res;
			return res;
		}
		public bool IsEqualGroupValues(object val1, object val2) {
			if(val1 == null || val2 == null || val1 is DBNull || val2 is DBNull) {
				return val1 == val2;
			}
			return IsEqualNonNullValues(val1, val2);
		}
		protected virtual bool IsEqualNonNullValues(object val1, object val2) {
			return val1.Equals(val2);
		}
		public void ExpandRow(int groupRowHandle) {
			ExpandRow(groupRowHandle, false);
		}
		public void ExpandRow(int groupRowHandle, bool recursive) {
			ChangeExpanded(groupRowHandle, true, recursive);
		}
		public void CollapseRow(int groupRowHandle) {
			CollapseRow(groupRowHandle, false);
		}
		public void CollapseRow(int groupRowHandle, bool recursive) {
			ChangeExpanded(groupRowHandle, false, recursive);
		}
		public void ExpandAll() {
			ChangeAllExpanded(true);
		}
		public bool IsRowExpanded(int groupRowHandle) {
			GroupRowInfo group = GroupInfo.GetGroupRowInfoByControllerRowHandle(groupRowHandle);
			return group == null ? false : group.Expanded;
		}
		public void CollapseAll() {
			ChangeAllExpanded(false);
		}
		protected virtual void ChangeExpanded(int groupRowHandle, bool expanded, bool recursive) {
			if(GroupInfo.ChangeExpanded(groupRowHandle, expanded, recursive)) {
				BuildVisibleIndexes();
				VisualClientUpdateLayout();
			}
		}
		protected virtual void ChangeAllExpanded(bool expanded) {
			if(GroupInfo.ChangeAllExpanded(expanded)) {
				BuildVisibleIndexes();
				VisualClientUpdateLayout();
			}
		}
		protected void BuildVisibleIndexes() {
			VisibleRowIndexes.BuildVisibleIndexes(false);
		}
		protected virtual DataHelperBase CreateDataHelper() {
			IList list = DataSource as IList;
			if(list != null)
				return new DataHelperList(this, list);
			IEnumerable enumerable = DataSource as IEnumerable;
			if(enumerable != null)
				return new DataHelperList(this, CreateListHelper(enumerable));
			return new DataHelperBase(this);
		}
		protected internal virtual int GetListSourceRowIndex(int controllerRow) {
			return ControllerRowIndexes.GetListSourceIndex(controllerRow);
		}
		protected virtual ValueComparer CreateComparer() { return new ValueComparer(); }
		protected virtual GroupRowInfoCollection CreateGroupInfo() { return new GroupRowInfoCollection(SortInfo); }
		IList CreateListHelper(IEnumerable enumerable) {
			List<object> res = new List<object>();
			foreach(object obj in enumerable)
				res.Add(obj);
			return res;
		}
		protected virtual void OnDataSourceChanged() {
			this.helper = null;
			VisualClient.OnDataSourceChanging();
			DoRefresh();
			CheckFocusedControllerRow();
		}	
	}
	public class EditingRowInfo {
		object editingRow;
		int controllerRow;
		int listIndex;
		Dictionary<DataColumn, object> savedValues;
		public EditingRowInfo(int controllerRow, int listIndex, object editingRow) {
			this.editingRow = editingRow;
			this.controllerRow = controllerRow;
			this.listIndex = listIndex;
			this.savedValues = new Dictionary<DataColumn, object>();
		}
		public object EditingRow { get { return editingRow; } }
		public int ControllerRow { get { return controllerRow; } }
		public int ListIndex { get { return listIndex; } }
		public Dictionary<DataColumn, object> SavedValues { get { return savedValues; } }
	}
	public interface IDataControllerVisualClient {
		void OnDataSourceChanging();
		void UpdateLayout();
		void OnBeforeRefresh();
		bool RowDeleting(int listSourceRow);
		void RowDeleted(int listSourceRow);
		object CalcCustomSummary(object sender, CustomSummaryEventArgs summaryCalculateArgs);
		bool IsCustomSummaryExists(GroupRowInfo groupRow, SummaryItem summaryItem);
		bool RowEditing(int controllerRow);
		void RowEdited(int controllerRow, Dictionary<DataColumn, object> savedValues, bool cancel);
		void SelectionChanged(SelectionChangedEventArgs e);
		bool FocusedRowChanging(int oldControllerRow, int newControllerRow);
		void FocusedRowChanged(int oldControllerRow, int newControllerRow);
	}
	class NullVisualClient : IDataControllerVisualClient {
		public virtual void OnDataSourceChanging() { }
		public virtual void UpdateLayout() { }
		public virtual void OnBeforeRefresh() { }
		public virtual bool RowDeleting(int listSourceRow) { return true; }
		public virtual void RowDeleted(int listSourceRow) { }
		public virtual object CalcCustomSummary(object sender, CustomSummaryEventArgs summaryCalculateArgs) { return null; }
		public virtual bool IsCustomSummaryExists(GroupRowInfo groupRow, SummaryItem summaryItem) { return true; }
		public virtual bool RowEditing(int controllerRow) { return true; }
		public virtual void RowEdited(int controllerRow, Dictionary<DataColumn, object> savedValues, bool cancel) { }
		public virtual void SelectionChanged(SelectionChangedEventArgs e) { }
		public virtual bool FocusedRowChanging(int oldControllerRow, int newControllerRow) { return true; }
		public virtual void FocusedRowChanged(int oldControllerRow, int newControllerRow) { }
	}
}
